// Content script to detect HLS videos on web pages

// Store detected videos to prevent duplicates
const detectedVideos = new Set();

// Function to detect HLS videos
function detectHLSVideos() {
  const hlsVideos = [];
  
  // Method 1: Analyze video elements in the DOM
  const videoElements = document.querySelectorAll('video');
  
  videoElements.forEach((video, index) => {
    // Check if the video source is an m3u8 file
    if (video.src && (video.src.includes('.m3u8') || isHLSUrl(video.src))) {
      const videoId = `hls-${getVideoId(video.src)}-${index}`;
      if (!detectedVideos.has(videoId)) {
        detectedVideos.add(videoId);
        hlsVideos.push({
          id: videoId,
          url: video.src,
          title: document.title,
          pageUrl: window.location.href
        });
      }
    }
    
    // Check source elements within video tags
    const sourceElements = video.querySelectorAll('source');
    sourceElements.forEach((source, sourceIndex) => {
      if (source.src && (source.src.includes('.m3u8') || isHLSUrl(source.src))) {
        const videoId = `hls-${getVideoId(source.src)}-${index}-${sourceIndex}`;
        if (!detectedVideos.has(videoId)) {
          detectedVideos.add(videoId);
          hlsVideos.push({
            id: videoId,
            url: source.src,
            title: document.title,
            pageUrl: window.location.href
          });
        }
      }
    });
    
    // Check for hls.js instances attached to video elements
    if (video.hls && video.hls.url) {
      const videoId = `hls-${getVideoId(video.hls.url)}-${index}-hlsjs`;
      if (!detectedVideos.has(videoId)) {
        detectedVideos.add(videoId);
        hlsVideos.push({
          id: videoId,
          url: video.hls.url,
          title: document.title,
          pageUrl: window.location.href
        });
      }
    }
    
    // Check for Aliplayer instances (used by GeekBang)
    if (video.aliplayer && video.aliplayer.config && video.aliplayer.config.source) {
      const source = video.aliplayer.config.source;
      if (isHLSUrl(source)) {
        const videoId = `hls-${getVideoId(source)}-${index}-aliplayer`;
        if (!detectedVideos.has(videoId)) {
          detectedVideos.add(videoId);
          hlsVideos.push({
            id: videoId,
            url: source,
            title: document.title,
            pageUrl: window.location.href
          });
        }
      }
    }
    
    // Enhanced check for Aliplayer instances (used by GeekBang)
    // Sometimes the source is in a different structure
    if (video.aliplayer) {
      // Check for source in different possible locations
      let source = null;
      
      // Try different possible locations for the source URL
      if (video.aliplayer.config && video.aliplayer.config.source) {
        source = video.aliplayer.config.source;
      } else if (video.aliplayer.options && video.aliplayer.options.source) {
        source = video.aliplayer.options.source;
      } else if (video.dataset && video.dataset.source) {
        source = video.dataset.source;
      }
      
      if (source && isHLSUrl(source)) {
        const videoId = `hls-${getVideoId(source)}-${index}-aliplayer-enhanced`;
        if (!detectedVideos.has(videoId)) {
          detectedVideos.add(videoId);
          hlsVideos.push({
            id: videoId,
            url: source,
            title: document.title,
            pageUrl: window.location.href
          });
        }
      }
    }
  });
  
  // Method 2: Look for HLS-related JavaScript variables
  const scripts = document.querySelectorAll('script');
  scripts.forEach((script, index) => {
    if (script.src && isHLSUrl(script.src)) {
      const videoId = `hls-${getVideoId(script.src)}-script-${index}`;
      if (!detectedVideos.has(videoId)) {
        detectedVideos.add(videoId);
        hlsVideos.push({
          id: videoId,
          url: script.src,
          title: document.title,
          pageUrl: window.location.href
        });
      }
    }
    
    // Check inline scripts for HLS references
    if (script.textContent) {
      // Try to extract URLs from the script content
      const hlsMatches = script.textContent.match(/['"]([^'"]+\.(m3u8|mp4|webm|ogg)[^'"]*)['"]/gi);
      if (hlsMatches) {
        hlsMatches.forEach((match, matchIndex) => {
          // Remove quotes from the match
          const url = match.replace(/['"]/g, '');
          // Try to decode if it looks like Base64
          const decodedUrl = tryDecodeBase64Url(url);
          if (isHLSUrl(decodedUrl)) {
            const videoId = `hls-${getVideoId(decodedUrl)}-script-inline-${index}-${matchIndex}`;
            if (!detectedVideos.has(videoId)) {
              detectedVideos.add(videoId);
              hlsVideos.push({
                id: videoId,
                url: decodedUrl,
                title: document.title,
                pageUrl: window.location.href
              });
            }
          }
        });
      }
    }
  });
  
  // Method 3: Check for HLS player libraries
  // Look for common HLS player libraries like hls.js
  if (window.Hls || window.Clappr || window.VideoJS || window.Aliplayer) {
    try {
      // Check for hls.js
      if (window.Hls) {
        // Look for hls.js instances in video elements
        videoElements.forEach((video, index) => {
          if (video.hls && video.hls.url && isHLSUrl(video.hls.url)) {
            const videoId = `hls-${getVideoId(video.hls.url)}-${index}-hlsjs-instance`;
            if (!detectedVideos.has(videoId)) {
              detectedVideos.add(videoId);
              hlsVideos.push({
                id: videoId,
                url: video.hls.url,
                title: document.title,
                pageUrl: window.location.href
              });
            }
          }
        });
      }
      
      // Check for VideoJS with HLS support
      if (window.videojs) {
        videoElements.forEach((video, index) => {
          if (video.player && video.player.techName_ === 'Html5' && video.player.hls) {
            const playlist = video.player.hls.playlists && video.player.hls.playlists.master && 
                            video.player.hls.playlists.master.playlists && 
                            video.player.hls.playlists.master.playlists[0] && 
                            video.player.hls.playlists.master.playlists[0].resolvedUri;
            if (playlist && isHLSUrl(playlist)) {
              const videoId = `hls-${getVideoId(playlist)}-${index}-videojs`;
              if (!detectedVideos.has(videoId)) {
                detectedVideos.add(videoId);
                hlsVideos.push({
                  id: videoId,
                  url: playlist,
                  title: document.title,
                  pageUrl: window.location.href
                });
              }
            }
          }
        });
      }
      
      // Check for Aliplayer (used by GeekBang)
      if (window.Aliplayer) {
        videoElements.forEach((video, index) => {
          if (video.aliplayer && video.aliplayer.config && video.aliplayer.config.source) {
            const source = video.aliplayer.config.source;
            if (isHLSUrl(source)) {
              const videoId = `hls-${getVideoId(source)}-${index}-aliplayer-global`;
              if (!detectedVideos.has(videoId)) {
                detectedVideos.add(videoId);
                hlsVideos.push({
                  id: videoId,
                  url: source,
                  title: document.title,
                  pageUrl: window.location.href
                });
              }
            }
          }
        });
      }
      
      // Enhanced check for Aliplayer (used by GeekBang)
      if (window.Aliplayer) {
        videoElements.forEach((video, index) => {
          // Check for source in different possible locations
          let source = null;
          
          // Try different possible locations for the source URL
          if (video.aliplayer && video.aliplayer.config && video.aliplayer.config.source) {
            source = video.aliplayer.config.source;
          } else if (video.aliplayer && video.aliplayer.options && video.aliplayer.options.source) {
            source = video.aliplayer.options.source;
          } else if (video.dataset && video.dataset.source) {
            source = video.dataset.source;
          }
          
          if (source && isHLSUrl(source)) {
            const videoId = `hls-${getVideoId(source)}-${index}-aliplayer-enhanced-global`;
            if (!detectedVideos.has(videoId)) {
              detectedVideos.add(videoId);
              hlsVideos.push({
                id: videoId,
                url: source,
                title: document.title,
                pageUrl: window.location.href
              });
            }
          }
        });
      }
    } catch (e) {
      console.log('Error checking for HLS libraries:', e);
    }
  }
  
  // Method 4: Check for common HLS-related global variables
  const hlsGlobals = ['hlsUrl', 'hlsSource', 'm3u8Url', 'streamUrl', 'playlistUrl', 'manifestUrl'];
  hlsGlobals.forEach((globalVar, index) => {
    if (window[globalVar] && typeof window[globalVar] === 'string' && isHLSUrl(window[globalVar])) {
      const videoId = `hls-${getVideoId(window[globalVar])}-global-${index}`;
      if (!detectedVideos.has(videoId)) {
        detectedVideos.add(videoId);
        hlsVideos.push({
          id: videoId,
          url: window[globalVar],
          title: document.title,
          pageUrl: window.location.href
        });
      }
    }
  });
  
  // Method 5: Check for objects that might contain HLS URLs
  try {
    Object.keys(window).forEach(key => {
      try {
        // Check for objects with HLS-related names
        if (key.toLowerCase().includes('hls') || key.toLowerCase().includes('video') || 
            key.toLowerCase().includes('player') || key.toLowerCase().includes('stream')) {
          const obj = window[key];
          if (obj && typeof obj === 'object') {
            // Check common properties
            const propsToCheck = ['url', 'src', 'source', 'playlist', 'manifest', 'hlsUrl', 'm3u8'];
            propsToCheck.forEach(prop => {
              if (obj[prop] && typeof obj[prop] === 'string' && isHLSUrl(obj[prop])) {
                const videoId = `hls-${getVideoId(obj[prop])}-global-${key}-${prop}`;
                if (!detectedVideos.has(videoId)) {
                  detectedVideos.add(videoId);
                  hlsVideos.push({
                    id: videoId,
                    url: obj[prop],
                    title: document.title,
                    pageUrl: window.location.href
                  });
                }
              }
            });
            
            // Recursively check nested objects
            Object.keys(obj).forEach(nestedKey => {
              if (typeof obj[nestedKey] === 'object' && obj[nestedKey] !== null) {
                propsToCheck.forEach(prop => {
                  if (obj[nestedKey][prop] && typeof obj[nestedKey][prop] === 'string' && isHLSUrl(obj[nestedKey][prop])) {
                    const videoId = `hls-${getVideoId(obj[nestedKey][prop])}-global-${key}-${nestedKey}-${prop}`;
                    if (!detectedVideos.has(videoId)) {
                      detectedVideos.add(videoId);
                      hlsVideos.push({
                        id: videoId,
                        url: obj[nestedKey][prop],
                        title: document.title,
                        pageUrl: window.location.href
                      });
                    }
                  }
                });
              }
            });
          }
        }
      } catch (e) {
        // Ignore errors when accessing window properties
      }
    });
  } catch (e) {
    console.log('Error checking for global HLS variables:', e);
  }
  
  return hlsVideos;
}

// Helper function to check if a URL is an HLS URL
function isHLSUrl(url) {
  if (!url || typeof url !== 'string') return false;
  
  // Check for common HLS indicators
  const hlsIndicators = ['.m3u8', 'hls', 'stream', 'playlist', 'manifest'];
  return hlsIndicators.some(indicator => url.toLowerCase().includes(indicator));
}

// Helper function to try decoding Base64 URLs
function tryDecodeBase64Url(url) {
  if (!url || typeof url !== 'string') return url;
  
  // Check if it looks like Base64 (no URL-specific characters)
  if (!url.includes('/') && !url.includes(':') && url.length > 20) {
    try {
      // Try to decode as Base64
      const decoded = atob(url);
      // Check if the decoded string looks like a URL
      if (decoded.includes('http') || decoded.includes('.m3u8')) {
        return decoded;
      }
    } catch (e) {
      // Not Base64, return original
      return url;
    }
  }
  
  return url;
}

// Generate a unique ID for a video based on its URL
function getVideoId(url) {
  // Extract the base URL without query parameters
  try {
    const urlObj = new URL(url);
    const base = urlObj.origin + urlObj.pathname;
    // Create a simple hash of the base URL
    let hash = 0;
    for (let i = 0; i < base.length; i++) {
      const char = base.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return Math.abs(hash).toString(36);
  } catch (e) {
    // If URL parsing fails, use a simplified approach
    return btoa(url).replace(/[^a-zA-Z0-9]/g, '').substring(0, 10);
  }
}

// Send detected videos to background script
function sendVideosToBackground(videos) {
  if (videos.length > 0) {
    // Get the current tab ID from the background script
    chrome.runtime.sendMessage({action: 'getCurrentTabId'}, (response) => {
      if (chrome.runtime.lastError) {
        console.error('Error getting current tab ID:', chrome.runtime.lastError);
        // Fallback to sending without tabId
        chrome.runtime.sendMessage({
          action: 'hlsVideosDetected',
          videos: videos
        });
      } else {
        // Send videos with the correct tab ID
        chrome.runtime.sendMessage({
          action: 'hlsVideosDetected',
          videos: videos,
          tabId: response.tabId
        });
      }
    });
  }
}

// Run detection immediately when the script loads
const detectedVideosInitial = detectHLSVideos();
console.log('Initial HLS videos detected:', detectedVideosInitial);
sendVideosToBackground(detectedVideosInitial);

// Also run detection periodically to catch dynamically loaded content
setInterval(() => {
  const newVideos = detectHLSVideos();
  if (newVideos.length > 0) {
    console.log('Periodic detection found new videos:', newVideos);
    sendVideosToBackground(newVideos);
  }
}, 3000);

// Listen for DOM changes to catch dynamically loaded content
const observer = new MutationObserver((mutations) => {
  let shouldDetect = false;
  
  for (let mutation of mutations) {
    if (mutation.type === 'childList') {
      // Check if video elements were added
      mutation.addedNodes.forEach(node => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.tagName === 'VIDEO' || node.querySelector('video')) {
            shouldDetect = true;
          }
        }
      });
      
      // Check if script elements were added
      mutation.addedNodes.forEach(node => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.tagName === 'SCRIPT' || node.querySelector('script')) {
            shouldDetect = true;
          }
        }
      });
    }
  }
  
  if (shouldDetect) {
    // Small delay to allow content to initialize
    setTimeout(() => {
      const newVideos = detectHLSVideos();
      if (newVideos.length > 0) {
        sendVideosToBackground(newVideos);
      }
    }, 500);
  }
});

// Start observing
observer.observe(document.body, {
  childList: true,
  subtree: true
});

// Listen for messages from the background script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'getHlsVideos') {
    const videos = detectHLSVideos();
    sendResponse({ videos: videos });
  }
  return true; // Keep the message channel open for async response
});

// Override XMLHttpRequest to catch HLS requests
(function() {
  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;
  
  XMLHttpRequest.prototype.open = function(method, url) {
    this._url = url; // Store the URL for later use
    return originalOpen.apply(this, arguments);
  };
  
  XMLHttpRequest.prototype.send = function() {
    if (this._url && isHLSUrl(this._url)) {
      console.log('HLS XMLHttpRequest detected:', this._url);
      
      // Try to detect videos after a short delay
      setTimeout(() => {
        const newVideos = detectHLSVideos();
        sendVideosToBackground(newVideos);
      }, 1000);
    }
    
    // Also listen for the response
    this.addEventListener('load', function() {
      try {
        // Check if response contains HLS URLs
        const responseText = this.responseText;
        if (responseText && (responseText.includes('.m3u8') || responseText.includes('hls') || responseText.includes('stream'))) {
          // Try to extract URLs from the response
          const urlMatches = responseText.match(/https?:\/\/[^"' ]+\.m3u8[^"' ]*/g);
          if (urlMatches) {
            urlMatches.forEach(url => {
              if (isHLSUrl(url)) {
                const videoId = `hls-${getVideoId(url)}-xhr-response`;
                if (!detectedVideos.has(videoId)) {
                  detectedVideos.add(videoId);
                  const video = {
                    id: videoId,
                    url: url,
                    title: document.title,
                    pageUrl: window.location.href
                  };
                  sendVideosToBackground([video]);
                }
              }
            });
          }
        }
      } catch (e) {
        // Ignore errors when parsing response
      }
      
      // Even if we didn't find HLS URLs in the response, run detection
      // as the request might have triggered changes in the DOM
      setTimeout(() => {
        const newVideos = detectHLSVideos();
        sendVideosToBackground(newVideos);
      }, 500);
    });
    
    return originalSend.apply(this, arguments);
  };
  
  // Also override fetch
  const originalFetch = window.fetch;
  window.fetch = function(input, init) {
    let url = typeof input === 'string' ? input : input && input.url ? input.url : '';
    const isHLS = isHLSUrl(url);
    
    if (isHLS) {
      console.log('HLS fetch request detected:', url);
      // Try to detect videos after a short delay
      setTimeout(() => {
        const newVideos = detectHLSVideos();
        sendVideosToBackground(newVideos);
      }, 1000);
    }
    
    // Call the original fetch method and listen for the response
    const fetchPromise = originalFetch.apply(this, arguments);
    
    // If it's an HLS request or we want to check all responses
    fetchPromise.then(response => {
      // Clone the response to avoid locking the original
      const clonedResponse = response.clone();
      
      // Check if the response is text-based and might contain HLS URLs
      const contentType = clonedResponse.headers.get('content-type');
      if (contentType && (contentType.includes('application/json') || contentType.includes('text/'))) {
        clonedResponse.text().then(text => {
          if (text && (text.includes('.m3u8') || text.includes('hls') || text.includes('stream'))) {
            // Try to extract URLs from the response
            const urlMatches = text.match(/https?:\/\/[^"' ]+\.m3u8[^"' ]*/g);
            if (urlMatches) {
              urlMatches.forEach(url => {
                if (isHLSUrl(url)) {
                  const videoId = `hls-${getVideoId(url)}-fetch-response`;
                  if (!detectedVideos.has(videoId)) {
                    detectedVideos.add(videoId);
                    const video = {
                      id: videoId,
                      url: url,
                      title: document.title,
                      pageUrl: window.location.href
                    };
                    sendVideosToBackground([video]);
                  }
                }
              });
            }
          }
        }).catch(e => {
          // Ignore errors when parsing response
        });
      }
      
      // Even if we didn't find HLS URLs in the response, run detection
      // as the request might have triggered changes in the DOM
      setTimeout(() => {
        const newVideos = detectHLSVideos();
        sendVideosToBackground(newVideos);
      }, 500);
    }).catch(e => {
      // Ignore errors when processing fetch response
      // But still run detection in case the request failure 
      // triggered some changes in the DOM
      setTimeout(() => {
        const newVideos = detectHLSVideos();
        sendVideosToBackground(newVideos);
      }, 500);
    });
    
    return fetchPromise;
  };
})();